/*
Copyright 2007 Julian Asamer

This file is part of Fractalicious.

    Fractalicious is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Fractalicious is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Fractalicious.  If not, see <http://www.gnu.org/licenses/>.
*/


#import <UIKit/UIKit.h>
#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <UIKit/CDStructures.h>
#import <UIKit/UIPushButton.h>
#import <UIKit/UIThreePartButton.h>
#import <UIKit/UINavigationBar.h>
#import <UIKit/UIWindow.h>
#import <UIKit/UIView-Hierarchy.h>
#import <UIKit/UIHardware.h>
#import <UIKit/UITable.h>
#import <UIKit/UITableCell.h>
#import <UIKit/UITableColumn.h>
#import "FView.h"
#import <GraphicsServices/GraphicsServices.h>
#import "FractalController.h"
#import "DrawingData.h"
#import "Vector.h"
#import "Dragable.h"
#import "FOptions.h"

@implementation FView 

	+ (CGColorRef)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha {
		float rgba[4] = {red, green, blue, alpha};
		CGColorSpaceRef rgbColorSpace = (CGColorSpaceRef)[(id)CGColorSpaceCreateDeviceRGB() autorelease];
		CGColorRef color = (CGColorRef)[(id)CGColorCreate(rgbColorSpace, rgba) autorelease];
		return color;
	}

	-(id) init {
		[super initWithFrame: CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
		[self setTapDelegate: self];
		acat = [[NSAffineTransform transform] retain];
		return self;
	}

	-(void) reset {
		[acat release];
		acat = [[NSAffineTransform alloc] init];
	}

	-(void) addOptionsToFOptions: (FOptions *)table {
		FRGBColor foregroundcolor = {1,1,1,1};
		FRGBColor backgroundcolor = {0,0,0,1};
		
		fgcolor = [table addColorWithColor: foregroundcolor title: @"Foreground color" type: Drawonly];
		bgcolor = [table addColorWithColor: backgroundcolor title: @"Background color" type: Drawonly];
		stroke = [table addSliderWithMin: 0.01 max: 20 value: 1 title: @"Stroke" type: Drawonly];
		dyn_stroke = [table addCheckboxWithTitle: @"Dynamic stroke" selected: NO type: Drawonly];
		dr_draggables = [table addCheckboxWithTitle: @"Draw draggables" selected: YES type: Drawonly];
		dyn_color =  [table addCheckboxWithTitle: @"Cycle colors" selected: NO type: Drawonly];
		dyn_color_f = [table addSliderWithMin: 0.001 max: 0.05 value: 1 title: @"Color cycle factor" type: Drawonly];
		only_last = [table addCheckboxWithTitle: @"Draw only last rec" selected: NO type: Drawonly];
		first_not = [table addCheckboxWithTitle: @"Don't draw first rec" selected: NO type: Drawonly];
	}

	-(void) setFractalData: (DrawableFractal *)df {
		data = df;
	}
	-(id)fractalData {
		return data;
	}


	-view:(id) view handleTapWithCount:(int) count event: (GSEvent *) event {
		NSLog(@"Handled tap");
		[[FractalController sharedInstance] toggleBars];
	}
	
	- (BOOL)ignoresMouseEvents {
	    return NO;
	}

	- (BOOL)canHandleGestures {
	   return YES;
	}

//Check if a dragable was pressed, if not, move.	
	-(void) mouseDown: (GSEvent *) event {
		NSMutableArray * dg = [delegate getDragables];
		int i;
		lastFingerPos = [[Point2D alloc] initWithx: GSEventGetLocationInWindow(event).x 
												 y:	GSEventGetLocationInWindow(event).y ];
		
		for (i=0; i<[dg count]; i++) {
			if ([[dg objectAtIndex: i] respondsToSelector: @selector(pointInside:)]) {
				NSAffineTransform * inv = [[[NSAffineTransform alloc] initWithTransform: 
					acat] autorelease];
				[inv invert];
				NSSize sz;
				sz.height = sz.width = 32;
				sz = [inv transformSize: sz];
		
				if ([[dg objectAtIndex: i] equals: [[[Point2D alloc] initWithNSPoint: [inv transformPoint: [lastFingerPos nsPoint]]]  
				autorelease] withMaxDistance: sqrt(sz.width*sz.width+sz.height*sz.height)]) {
					draggedDragable = [dg objectAtIndex: i];
				}
			}
		}
	}
	
	-(void) mouseUp: (GSEvent *) event {
		if ([draggedDragable respondsToSelector: @selector(handleMouseUp)]) 
			[draggedDragable handleMouseUp];
		draggedDragable = nil;
		[lastFingerPos release];
	}

	-(void) mouseDragged: (GSEvent *) event {
		Point2D * acPos = [[Point2D alloc] initWithx: 
						GSEventGetLocationInWindow(event).x 
						y: GSEventGetLocationInWindow(event).y];
		if (draggedDragable != nil) {		
			if ([draggedDragable respondsToSelector: @selector(handleMove:)]) {
				NSAffineTransform * inv = [[[NSAffineTransform alloc] initWithTransform: 
					acat] autorelease];
				[inv invert];
				[draggedDragable handleMove: [[[Vector2D alloc] initWithNSSize: 
						[inv transformSize:[[lastFingerPos to: acPos] nsSize]]] 
						autorelease]];
			} 
		} else {
			Vector2D *mv = [lastFingerPos to: acPos];
			NSAffineTransform * inv = [[NSAffineTransform alloc] initWithTransform: acat];
			[inv invert];
			NSSize tmv = [inv transformSize: [mv nsSize]];
			[acat translateXBy: tmv.width yBy: tmv.height];
			[self setNeedsDisplay];
		}
		[lastFingerPos release];
		lastFingerPos = acPos;
	}
	
	-(void) gestureChanged: (GSEvent *) event {
		//Some qute nice Code follows.
		//acPos ( = the position right now) is in this case a Line2D; which is defined by its two points p and q. 
		Line2D *acPos = [[Line2D alloc] initWithpx: 
						GSEventGetInnerMostPathPosition(event).x
					py:	GSEventGetInnerMostPathPosition(event).y
					qx:	GSEventGetOuterMostPathPosition(event).x
					qy:	GSEventGetOuterMostPathPosition(event).y];
		
		//If none of both points moved more than two pixels, do nothing.
		if (!([acPos->p equals: lastPos->p withMaxDistance: 2.0f] && 
					[acPos->q equals: lastPos->q withMaxDistance: 2.0f])) {
			
			//Zoom is easy.
			float zoom = 1;
			if ([[acPos vector] length]>0) 
				zoom = [[acPos vector] length] / [[lastPos vector] length];
			//Rotation is easy too, thanks to my vector class :-) and atan2.
			float rotation = [[acPos vector] angleBetweenVector: [lastPos vector]];
			
			//The move vector.
			Vector2D *mv = [[lastPos middle] to: [acPos middle]];
			//The "middle" between the two lines (it's the middle of the line defined by the middles of both lines); 
			//the point where I want to do the scaling + rotating
			Point2D * middle = [[[[Line2D alloc] initWithPoint: [acPos middle] 
				andPoint: [lastPos middle]] autorelease] middle];
			
			
			
			//Ok, let's go.
			//First we need to transform the finger input to the fractal's coordinate system.
			NSAffineTransform * inv = [[NSAffineTransform alloc] initWithTransform: acat];
			//So we need an inverted Matrix (acat transforms form the fractals coordinate system to the screen's),
			[inv invert];
			NSSize tmv = [inv transformSize: [mv nsSize]];
			NSPoint tmiddle = [inv transformPoint: [middle nsPoint]];
			[inv release];
			
			//Now we can start with scaling/rotating
			//I want to do the scaling and rotating in the middle
			//of both lines, so lets set it to the origin.
			[acat translateXBy: (tmiddle.x) yBy: (tmiddle.y)];
			//Now, let's rotate & scale.
			[acat rotateByRadians: rotation];
			[acat scaleBy: zoom];
			
			//And move back. We also wanted to move a bit...
			[acat translateXBy: (-tmiddle.x + tmv.width) yBy: (-tmiddle.y + tmv.height)];
			
			//We're done!
			
			if ([delegate respondsToSelector: @selector(viewChanged)])
				[delegate viewChanged: acat];
				
			[lastPos release];
			lastPos = acPos;
			[lastPos retain];
			[self setNeedsDisplay];
		} 
	}
	-(void) gestureStarted: (GSEvent *) event {
		lastPos = [[Line2D alloc] initWithpx: 
						GSEventGetInnerMostPathPosition(event).x
					py:	GSEventGetInnerMostPathPosition(event).y
					qx:	GSEventGetOuterMostPathPosition(event).x
					qy:	GSEventGetOuterMostPathPosition(event).y];
	}
	-(void) gestureEnded: (GSEvent *) event {
	}

	-(void) drawRect:(CGRect)rect {
		
		//DrawingData *data = [DrawingData sharedInstance];
		//NSLog(@"Starting to draw!");
			float color[] = {1.0f, 0.5f, 0.0f, 1.0f};
			CGContextRef context = UICurrentContext();
			
			CGContextSetFillColorWithColor(context, [ColorUtils colorWithFRGBColor: [bgcolor controlValue]]);
			CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 320.0f, 480.0f));
			
			if (data != nil) {
				//NSLog(@"Data is NOT nil.");
				//NSLog(@"Data has %i points.", sizeof(data->data));
				[data setFGColor: [fgcolor controlValue]
						dynamic: [dyn_color controlValue]
						dynamicColorFactor: [dyn_color_f controlValue]
						bGColor: [bgcolor controlValue]
						stroke: [stroke controlValue]
						dynamic: [dyn_stroke controlValue]
						onlyLast: [only_last controlValue]
						firstNot: [first_not controlValue]
				];
				//[data info];
				
				[data drawWithContext: context transform: acat];
				NSLog(@"Drawn.");
			} else {
				NSLog(@"Data is nil.");
			}

			int i;
			// 			//CGContextSetLineStyle(context,kCGCapBottom));
			// 			if (![dyn_color controlValue]) {
			// 				CGContextSetStrokeColor(context, color);
			// 				if (![dyn_stroke controlValue]) {
			// 					CGContextSetLineWidth(context, [stroke controlValue]);
			// 			///		NSLog(@"Redrawing with Stroke: %f", [stroke controlValue]);
			// 					Line2D *line;
			// 					for (i=0; i< data->actualLength; i++) {
			// 						line = [data->lines objectAtIndex: i];
			// 						//NSLog(@"length: %f", [line length]);
			// 					
			// 						NSPoint p = [acat transformPoint: [line->p nsPoint]];
			// 						NSPoint q = [acat transformPoint: [line->q nsPoint]];
			// 						float vx = q.x-p.x;
			// 						float vy = q.y-p.y;
			// 					
			// 						CGContextMoveToPoint(context, p.x,p.y);
			// 						CGContextAddLineToPoint(context, q.x,q.y);
			// 						CGContextStrokePath(context);
			// 					}
			// 				
			// 				} else { //dynStroke enabled.
			// 					int i;
			// 					Line2D *line;
			// 					for (i=0; i< data->actualLength; i++) {
			// 						line = [data->lines objectAtIndex: i];
			// 						//NSLog(@"length: %f", [line length]);
			// 					
			// 						NSPoint p = [acat transformPoint: [line->p nsPoint]];
			// 						NSPoint q = [acat transformPoint: [line->q nsPoint]];
			// 						float vx = q.x-p.x;
			// 						float vy = q.y-p.y;
			// 					
			// 						CGContextSetLineWidth(context, [stroke controlValue]*sqrt(vx*vx+vy*vy)/50);//  / 30.0f);
			// 	
			// 					
			// 						CGContextMoveToPoint(context, p.x,p.y);
			// 						CGContextAddLineToPoint(context, q.x,q.y);
			// 						CGContextStrokePath(context);
			// 					}
			// 				}
			// 			} else { //Dyn Color on
			// 				CGContextSetLineWidth(context, [stroke controlValue]);// sqrt(vx*vx+vy*vy) / 30.0f);
			// 				///		NSLog(@"Redrawing with Stroke: %f", [stroke controlValue]);
			// 				Line2D *line;
			// 				for (i=0; i< data->actualLength; i++) {
			// 					color[0]+=0.015f;
			// 					color[1]+=0.03f;
			// 					color[2]+=0.045;
			// 					//NSLog(@"Color: %f", color[0]);
			// 					if (color[0]>1) {color[0]-=1;}
			// 					if (color[1]>1) {color[1]-=1;}
			// 					if (color[2]>1) {color[2]-=1;}
			// 					
			// 					CGContextSetStrokeColor(context, color);					
			// 					line = [data->lines objectAtIndex: i];
			// 					//NSLog(@"length: %f", [line length]);
			// 					NSPoint p = [acat transformPoint: [line->p nsPoint]];
			// 					NSPoint q = [acat transformPoint: [line->q nsPoint]];
			// 					float vx = q.x-p.x;
			// 					float vy = q.y-p.y;
			//                 	
			//                 	
			// 					CGContextMoveToPoint(context, p.x,p.y);
			// 					CGContextAddLineToPoint(context, q.x,q.y);
			// 					CGContextStrokePath(context);
			// 				}
			// 			}
			if ([dr_draggables controlValue]&&[delegate respondsToSelector: @selector(getDragables)]) {
				NSMutableArray *dg = [delegate performSelector: @selector(getDragables)];
				//NSLog(@"Dragables: %i", [dg count]);
				//DragablePoint *point = [dg objectAtIndex: 0];
				//[point drawWithContext: context transform: acat];
				for (i=0; i<[dg count]; i++) {
					if ([[dg objectAtIndex: i] respondsToSelector:@selector(drawWithContext:transform:)])
						[[dg objectAtIndex: i] drawWithContext: context transform: acat];
				}
			}
				/*		int i;
		Line2D *line;
		for (i=0; i< [data->lines count]; i++) {
			line = [data->lines objectAtIndex: i];
			CGContextMoveToPoint(context, 50.0f,50.0f);
			CGContextAddLineToPoint(context, 100.0f,100.0f);
			
			
			//CGPathMoveToPoint(context, line->p->x, line->p->y);
			//CGContextAddLineToPoint(context, line->q->y, line->q->y);
		}*/
		
		
		
		// Make a CGRect that has its origin at (40,40)
		// with a width of 130 units and height of 100 units.
		//CGRect ourRect = CGRectMake(10, 10, 30, 30);



		//CGColorRef ref= [FView colorWithRed:1.0f green: 1.0f blue: 0.0f alpha: 0.5f];
		
		// Set the fill color to an opaque blue.
		//CGContextSetFillColorWithColor(context, [FView colorWithRed:0.0f green: 0.0f blue: 1.0f alpha: 0.5f]);
/*		int i=0;
		while (i<10) {
			int j=0;
			while (j<11) {
				CGContextFillRect(context, CGRectMake(10+i*30, 10+j++*40, 20, 20));
			}
			i++;
		}
		
		CGContextSetStrokeColorWithColor(context, [FView colorWithRed:0.0f green: 1.0f blue: 0.0f alpha: 0.5f]);
		
		i=0;
		while (i<10) {
			int j=0;
			while (j<11) {
				CGContextStrokeRectWithWidth(context, CGRectMake(10+i*30, 10+j++*40, 20, 20),i);
			}
			i++;
		}
		
		i=0;
		*/
	}

- (void) setDelegate: (id) d {
	delegate = d;
}
- (id) delegate {
	return delegate;
}
@end